import { Injectable, EventEmitter } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { MatDialog } from '@angular/material';
import { Observable } from 'rxjs/Observable';
import { KeycloakService } from './keycloak.service';
import { environment } from './../../environments/environment';
import { DialogTypes } from '../models/wdl-types';
import { DialogComponent } from '../dialog/dialog.component';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

import * as _ from 'lodash';

@Injectable()
export class RestService {
  private hostUrl = '';
  private currentApp;
  private tokenInterval;

  public eventEmit: EventEmitter<any>;

  constructor(private http: HttpClient,
              private dialog: MatDialog,
              private keycloakService: KeycloakService,
              private router: Router) {
    this.eventEmit = new EventEmitter();

    if (this.isDebuggingMode()) {
      this.hostUrl = '';
    }

    this.startRefreshToken();
  }

  startRefreshToken() {
    if (environment.debugging || !environment.doLoginFlag) {
      return;
    }

    if (this.tokenInterval) {
      clearInterval(this.tokenInterval);
    }

    // We set 60 seconds here since keycloak only update token just beore 90 seconds (we set it).
    this.tokenInterval = setInterval(() => {
      if (!environment.debugging && environment.doLoginFlag) {
        this.keycloakService.refreshToken();
      }
    }, 60000);
  }

  private attachAuthField(headers) {
    if (environment.doLoginFlag) {
      headers['Authorization'] = 'Bearer ' + this.keycloakService.getToken();
    }
  }

  private getCommonHeaders() {
    const headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'Content-Type': 'application/json'
    };

    this.attachAuthField(headers);

    return new HttpHeaders(headers);
  }

  private getDbLinkHeaders() {
    const headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'Content-Type': 'text/uri-list'
    };

    this.attachAuthField(headers);

    return new HttpHeaders(headers);
  }

  getUploadUrl() {
    return this.hostUrl + 'api/su/upgrade';
  }

  getUploadHeaders() {
    return {
      // 'Content-Type': 'multipart/form-data',
      'Authorization': 'Bearer ' + this.keycloakService.getToken()
    };
  }

  public getCsxf(): any {
    const headers = new HttpHeaders();
    headers.append('Content-Type', 'application/json');

    return this.http.get('/', {
      headers: headers,
      responseType: 'text',
      withCredentials: true
    });
  }

  public isDebuggingMode() {
    return environment.debugging;
  }

  public download(suffix: string) {
    return this.http.get(this.hostUrl + suffix, {
      responseType: 'blob',
      headers: this.getCommonHeaders()
    })
    .catch(this.handleDetailedError);
  }

  public getRaw(suffix: string) {
    return this.http.get(this.hostUrl + suffix, {
      headers: this.getCommonHeaders(),
      responseType: 'text'
    });
  }

  public get(suffix: string) {
    const myHeaders = this.getCommonHeaders();

    return this.http.get(this.hostUrl + suffix, {
      headers: myHeaders
    })
    .catch(this.handleDetailedError);
  }

  public getWithParams(suffix: string, params) {
    return this.http.get(this.hostUrl + suffix, {
      headers: this.getCommonHeaders(),
      params: params
    })
    .catch(this.handleDetailedError);
  }

  public post(suffix: string, body: any) {
    return this.http.post(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders()
    })
    .catch(this.handleDetailedError);
  }

  public postWithParams(suffix: string, params, body: any) {
    return this.http.post(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders(),
      params: params
    })
    .catch(this.handleDetailedError);
  }

  public put(suffix: string, body: any) {
    return this.http.put(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders()
    });
  }

  public link(fromUrl, toUrl) {
    return this.http.put(this.hostUrl + fromUrl, this.hostUrl + toUrl, {
      headers: this.getDbLinkHeaders()
    });
  }

  public patch(suffix: string, body: any) {
    return this.http.patch(this.hostUrl + suffix, body, {
      headers: this.getCommonHeaders()
    });
  }

  public delete(suffix: string) {
    return this.http.delete(this.hostUrl + suffix, {
      headers: this.getCommonHeaders()
    });
  }

  public deleteAll(urls: string[]) {
    const observableBatch = [];
    urls.forEach(url => {
      observableBatch.push(this.delete(url));
    })

    return Observable.forkJoin(observableBatch);
  }

  /**
   * To extrace data from RPC response.
   */
  private extractData(body) {
    /*
    if (!this.csfWidgets._.isEmpty(body)) {
      return this.csfWidgets._.values(body)[0];
    }
    */
  }

  /**
   * To return detailed data from response
   */
  private extractDetailedData(body) {
    /*
    if (!this.csfWidgets._.isEmpty(body)) {
      return this.csfWidgets._.values(body);
    }
    */
  }

  private forceLogout() {
    if (environment.logoutWhenAuthFail) {
      this.reload2Login();
    }
  }

  /**
   * This is to return detailed error reponse.
   */
  public handleDetailedError = (error: HttpErrorResponse | any) => {
    let body: {};
    if (error instanceof HttpErrorResponse) {
      if (error.status === 401) {
        this.forceLogout();
      } else if (error.status === 403
      && (error.message === 'Could not verify the provided CSRF token because your session was not found.')) {
        this.forceLogout();
      } else if (error.status === 0) {
        this.forceLogout();
      } else {
        const contentType = error.headers.get('content-type');
        if (contentType &&
            (contentType.indexOf('application/json') !== -1 ||
            contentType.indexOf('application/yang.errors+json') !== -1)) {
          body = error || {};
        } else {
          body = error;
        }
        return Observable.throw(body);
      }
    } else {
      body = error;
      return Observable.throw(body);
    }
  }

  public reload2Login() {
    sessionStorage.clear();

    if (environment.doLoginFlag) {
      this.keycloakService.logout();
    } else {
      this.router.navigateByUrl('');
      setTimeout(() => window.location.reload());
    }
  }

  /**
   * For debugging mode, convert ":" to "/" as former is not valid in file name.
   */
  private urlTranslate(input: string) {
    if (this.isDebuggingMode()) {
      return input.replace(/:/g, '/');
    }

    return input;
  }

  openDialog(type: DialogTypes, title: string, message: string) {
    this.openGenDialog(type, title, message, null);
  }

  openGenDialog(type: DialogTypes, title: string, message: string, callback) {
    this.dialog.open(DialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '450px',
      data: {
        type: type,
        title: title,
        message: message
      }
    }).afterClosed().subscribe(data => {
      if (data === 'Yes' && callback) {
        callback();
      }
    });
  }

  /**
   * title - show in title
   * error - construct error body
   */
  showHttpError(title, error: any) {
    if (!error) {
      this.openDialog(DialogTypes.ERROR, title, 'internalError');
      return;
    }

    if (error instanceof HttpErrorResponse) {
      let message = error.status + ' - ' + error.statusText + ': ' + error.url;
      if (error.error && error.error.message) {
        message += '; ' + error.error.message;
      }
      this.openDialog(DialogTypes.ERROR, title, message);
      return;
    }

   if (error.status) {
     this.openDialog(DialogTypes.ERROR, title, error.status + ' - ' + error.error + ': ' + error.message);
     return;
   }

   if (error.message) {
     this.openDialog(DialogTypes.ERROR, title, error.message);
     return;
   }
  }

  saveInstitution(institution) {
    sessionStorage.setItem('institution', JSON.stringify({
      id: institution.id,
      instName: institution.instName,
      ip: institution.ip
    }));
  }

  getInstitution() {
    const item = JSON.parse(sessionStorage.getItem('institution'));
    return item ? item : {};
  }

  getUser() {
    const item = JSON.parse(sessionStorage.getItem('user'));
    return item ? item : {};
  }

 /**
   * Update "sources" with new input "updates" with common key "id".
   */
  updateArray(sources, updates) {
    // Remove un-qualified source
    _.remove(sources, source => {
      return _.findIndex(updates, {id: source.id}) === -1;
    });

    updates.forEach((update, index) => {
      const oldIndex = _.findIndex(sources, {id: update.id});
      if (oldIndex === -1) {
        sources.splice(index, 0, update);
      } else {
        // update the existing old with new values
        _.assign(sources[oldIndex], update);
      }
    });
  }

  getAlarmEnumNumber(alarm) {
    switch (alarm) {
    case 'HEART':
      return 0;
    case 'BREATHE':
      return 1;
    case 'UP':
      return 2;
    case 'SIDE':
      return 3;
    case 'AWAY':
      return 4;
    case 'WET':
      return 5;
    case 'MOVE':
      return 6;
    case 'DEVICE':
      return 7;
    case 'RING':
      return 8;
    case 'LEFT':
      return 9;
    case 'RIGHT':
      return 10;
    case 'HIST_HEART':
      return 11;
    case 'HIST_BREATHE':
      return 12;
    case 'HIST_MOVE':
      return 13;
    case 'SLEEP':
      return 14;
    case 'TURN_OVER':
      return 15;
    }

    return -1;
  }

  setCurrentApp(app) {
    this.currentApp = app;
  }

  getCurrentApp() {
    return this.currentApp;
  }
}
